package ch.sv7.tool.dbmanager.db.difference;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import ch.sv7.tool.dbmanager.db.SchemaElement;
import ch.sv7.tool.dbmanager.db.difference.filter.DifferenceFilter;

/**
 * A class to group all schema differences
 *
 * @author svt
 *
 */
public class SchemaDifferences {

	//private List/*<SchemaDifference>*/ differences = new LinkedList();
	private List/*<DifferenceFilter>*/ filters = new ArrayList();
	private Map /*<String, List>*/ differencesGroupedBySchema = new TreeMap();
	
	public void addDifference(SchemaElement schema, SchemaDifference diff) {
		Iterator iter = filters.iterator();
		while (iter.hasNext()) {
			DifferenceFilter filter = (DifferenceFilter)iter.next();
			if (!filter.keep(diff)){
				return;
			}
		}
		
		List diffs = (List)differencesGroupedBySchema.get(schema.getSchemaIdentifier());
		if (diffs == null) {
			diffs = new ArrayList();
			differencesGroupedBySchema.put(schema.getSchemaIdentifier(), diffs);
		}
		diffs.add(diff);
	}
	
	public void addFirstDifferenceOfList(SchemaElement schema, List diffs) {
		FirstDifferenceOfList firstOnly = new FirstDifferenceOfList(diffs);
		Iterator iter = filters.iterator();
		while (iter.hasNext()) {
			DifferenceFilter filter = (DifferenceFilter)iter.next();
			firstOnly.applyFilter(filter);
		}
		
		if (firstOnly.getDifference() != null) {
			List diffList = (List)differencesGroupedBySchema.get(schema.getSchemaIdentifier());
			if (diffList == null) {
				diffList = new ArrayList();
				differencesGroupedBySchema.put(schema.getSchemaIdentifier(), diffList);
			}
			diffList.add(firstOnly);
		}
	}
	
	public void addFilter(DifferenceFilter filter) {
		
		filters.add(filter);
		Iterator listIter = differencesGroupedBySchema.values().iterator();
		while (listIter.hasNext()){
			List l = (List)listIter.next();
			Iterator iter = l.iterator();
			while (iter.hasNext()) {
				Object element = iter.next();
				if (element instanceof FirstDifferenceOfList) {
					FirstDifferenceOfList firstOnly = (FirstDifferenceOfList)element;
					firstOnly.applyFilter(filter);
				} else if (element instanceof SchemaDifference){
					SchemaDifference diff = (SchemaDifference)element;
					if (!filter.keep(diff)){
						iter.remove();
					}
				}
			}
		}
	}
	
	public int getSize() {
		return getDifferences().size();
	}
	
	public List getDifferences(){
		List diffs = new ArrayList();
		Iterator iter = differencesGroupedBySchema.values().iterator();
		while (iter.hasNext()) {
			List l = (List)iter.next();
			Iterator listIter = l.iterator();
			while (listIter.hasNext()){
				Object element = listIter.next();
				if (element instanceof FirstDifferenceOfList) {
					FirstDifferenceOfList firstOnly = (FirstDifferenceOfList)element;
					if (firstOnly.getDifference() != null){
						diffs.add(firstOnly.getDifference());
					}
				} else if (element instanceof SchemaDifference){
					SchemaDifference diff = (SchemaDifference)element;
					diffs.add(diff);
				}
				
			}
		}
		return diffs;
	}
	
	public boolean hasDifferences() {
		return getSize() > 0;
	}
	
	public boolean hasBlockingDifferences(){
		return getBlockingDifferencesSize() > 0;
	}
	
	public int getBlockingDifferencesSize() {
		int count = 0;
		List diffs = getDifferences();
		for (int i=0; i< diffs.size(); i++) {
			SchemaDifference diff = (SchemaDifference)diffs.get(i);
			if (diff.isBlocker()){
				count++;
			}
		}
		return count;
	}
	
	public SchemaDifference getDifference(int index) {
		return (SchemaDifference)getDifferences().get(index);
	}
	
	public void dump(PrintStream out) {
		List diffs = getDifferences();
		List messages = new ArrayList();
		for (int i = 0; i< diffs.size(); i++) {
			SchemaDifference diff = (SchemaDifference)diffs.get(i);
			boolean blocker = diff.isBlocker();
			messages.add((blocker ? "ERROR " : "WARN  ") + diff.getMessage());
		}
		Collections.sort(messages);
		for (int i=0; i<messages.size(); i++) {
			out.println(messages.get(i));
		}
		out.println("Number of differences found : " + diffs.size());
	}
	
	public class FirstDifferenceOfList {
		
		private List differences;

		public FirstDifferenceOfList(List differences) {
			super();
			this.differences = differences;
		}
		
		public SchemaDifference getDifference() {
			if (differences != null && differences.size() > 0){
				return (SchemaDifference)differences.get(0);
			}
			return null;
		}
		
		public void applyFilter(DifferenceFilter filter){
			if (differences == null || differences.size() == 0){
				return;
			}
			Iterator iter = differences.iterator();
			while (iter.hasNext()){
				SchemaDifference diff = (SchemaDifference)iter.next();
				if (!filter.keep(diff)){
					iter.remove();
				}
			}
		}
	}
}
